module Test
  module Unit
    class TestCase
      class_inheritable_accessor :before_setup_callbacks
      class_inheritable_accessor :after_setup_callbacks
      class_inheritable_accessor :before_teardown_callbacks
      class_inheritable_accessor :after_teardown_callbacks
      self.before_setup_callbacks = [ :setup_with_fixtures ]
      self.after_setup_callbacks = []
      self.before_teardown_callbacks = []
      self.after_teardown_callbacks = [ :teardown_with_fixtures ]
      
      class << self
        remove_method :method_added
        
        def before_setup( *args )
          args.each { |m| before_setup_callbacks << m }
        end
        
        def after_setup( *args )
          args.each { |m| after_setup_callbacks << m }
        end
        
        def before_teardown( *args )
          args.each { |m| before_teardown_callbacks << m }
        end
        
        def after_teardown( *args )
          args.each { |m| after_teardown_callbacks << m }
        end
      end
      
      def setup
        execute_before_setup_callbacks
        execute_after_setup_callbacks
      end
      
      def teardown
        execute_before_teardown_callbacks
        execute_after_teardown_callbacks
      end
      
      def execute_before_setup_callbacks
        ( self.class.before_setup_callbacks || [] ).each do |m|
          self.send m
        end
      end
      
      def execute_after_setup_callbacks
        ( self.class.after_setup_callbacks || [] ).each do |m|
          self.send m
        end
      end
      
      def execute_before_teardown_callbacks
        ( self.class.before_teardown_callbacks || [] ).each do |m|
          self.send m
        end
      end
      
      def execute_after_teardown_callbacks
        ( self.class.after_teardown_callbacks || [] ).each do |m|
          self.send m
        end
      end
      
      def self.method_added(method)
        case method.to_s
        when 'setup'
          unless method_defined?( :setup_without_callbacks )
            alias_method :setup_without_callbacks, :setup
            define_method( :setup ) do
              execute_before_setup_callbacks
              setup_without_callbacks
              execute_after_setup_callbacks
            end
          end
        when 'teardown'
          unless method_defined?( :teardown_without_callbacks )
            alias_method :teardown_without_callbacks, :teardown
            define_method( :teardown ) do
              execute_before_teardown_callbacks
              teardown_without_callbacks
              execute_after_teardown_callbacks
            end
          end
        end
      end
    end
  end
end